﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Xceed.Silverlight.Data;

namespace Xceed.Silverlight.DataGrid
{
  public partial class DataGridControl
  {

    private class ColumnUpdateManager : UpdateManagerBase
    {

      public ColumnUpdateManager( DataGridControl dataGridControl )
        : base( dataGridControl )
      {
      }

      private ColumnPanel ColumnPanel
      {
        get
        {
          return this.DataGridControl.DataGridPanel.ColumnPanel;
        }
      }

      protected override Orientation ContainerLayoutOrientation 
      {
        get
        {
          return Orientation.Horizontal;
        }
      }

      protected override object GetNavigationCurrent()
      {
        return this.DataGridControl.CurrentColumn;
      }

      protected override void SetCurrentElement( object element )
      {
        this.DataGridControl.CurrentColumn = ( Column )element;
      }

      protected override bool IsStubElement( object element )
      {
        return false;
      }

      protected override void InvalidateMeasure()
      {
        this.ColumnPanel.InvalidatePanelMeasure();
      }

      protected override Size MeasureContainer( Size availableSize, ViewportPosition viewPosition )
      {
        m_availableSize = availableSize;
        this.ColumnPanel.ScrollableLayout.SetViewportPosition( viewPosition );
        return this.ColumnPanel.Measure( availableSize );
      }

      protected override void CommitUpdate()
      {
        this.ColumnPanel.CommitColumns();
      }

      protected override void RollbackUpdate()
      {
        this.ColumnPanel.RollBackColumns();
      }

      protected override SourcePositionInfo ApplyReferenceUpdate( Size availableSize, ReferenceUpdateRequest updateRequest )
      {
        UpdateDetails updateDetails = updateRequest.UpdateDetails;
        object referenceObject = updateRequest.FetchPositionReference;
        long skip = updateRequest.Skip;
        FetchDirection fetchDirection = updateRequest.FetchDirection;

        List<Column> orderedColumns = this.DataGridControl.GetOrderedColumns().ToList();
        List<Column> leftFixedColumns = this.DataGridControl.GetFixedColumns( orderedColumns, true ).ToList();
        List<Column> rightFixedColumns = this.DataGridControl.GetFixedColumns( orderedColumns, false ).Reverse().ToList();
        double leftFixedColumnWidth = leftFixedColumns.Sum( ( col ) => col.ActualWidth );
        double rightFixedColumnWidth = rightFixedColumns.Sum( ( col ) => col.ActualWidth );
        double scrollableSpace = ( availableSize.Width - ( leftFixedColumnWidth + rightFixedColumnWidth ) );

        List<Column> scrollableColumns = this.DataGridControl.GetScrollableColumns( orderedColumns ).ToList();
        if( fetchDirection == FetchDirection.Backward )
        {
          scrollableColumns.Reverse();
          leftFixedColumns.Reverse();
          rightFixedColumns.Reverse();
          skip = -skip;
        }

        // If the referenceObject is a int, consider this as the column start position;
        // otherwise, it is the column itself. Null, start at zero.
        int firstColumnIndex;
        if( referenceObject is int )
        {
          Debug.Assert( fetchDirection == FetchDirection.Forward );
          firstColumnIndex = ( int )referenceObject;
        }
        else if( referenceObject is Column )
        {
          firstColumnIndex = scrollableColumns.IndexOf( ( Column )referenceObject );
        }
        else
        {
          Debug.Assert( referenceObject == null );
          firstColumnIndex = 0;
        }

        firstColumnIndex += Convert.ToInt32( skip );
        int startIndex = Math.Max( 0, Math.Min( firstColumnIndex, Math.Max( 0, scrollableColumns.Count - 1 ) ) );

        bool firstColumn = true;
        Func<Column, bool> hasAvailableSpace = ( col ) =>
        {
          if( firstColumn )
          {
            firstColumn = false;
            return true;
          }

          if( scrollableSpace <= 0 )
            return false;

          scrollableSpace -= col.ActualWidth;
          return true;
        };

        List<Column> neededScrollableColumns =
          scrollableColumns
          .Skip( startIndex )
          .TakeWhile( hasAvailableSpace ).ToList();

        m_layoutFirstColumnIndex = startIndex;

        bool updateSizeOrPositionOnly = updateDetails.ColumnSizeOrPositionOnly;

        List<ILayoutElement> rightFixedContainer = new List<ILayoutElement>();
        List<ILayoutElement> leftFixedContainer = new List<ILayoutElement>();
        List<ILayoutElement> neededScrollableContainers = new List<ILayoutElement>();

        foreach( Column column in rightFixedColumns )
        {
          rightFixedContainer.Add( this.DataGridControl.GetContainerForColumn( column, ColumnPosition.RightFixed, updateSizeOrPositionOnly ) );
        }

        foreach( Column column in leftFixedColumns )
        {
          leftFixedContainer.Add( this.DataGridControl.GetContainerForColumn( column, ColumnPosition.LeftFixed, updateSizeOrPositionOnly ) );
        }

        foreach( Column column in neededScrollableColumns )
        {
          neededScrollableContainers.Add( this.DataGridControl.GetContainerForColumn( column, ColumnPosition.Scrollable, updateSizeOrPositionOnly ) );
        }

        ReferencePoint referencePoint = ( fetchDirection == FetchDirection.Backward )
          ? ReferencePoint.AfterLast
          : ReferencePoint.BeforeFirst;
        RelativeOffset firstItemOffset = new RelativeOffset( startIndex + 1, referencePoint );
        EstimateInt64 totalCount = scrollableColumns.Count;
        bool containAllItems = ( neededScrollableColumns.Count == scrollableColumns.Count );
        object firstItemReference = neededScrollableColumns.FirstOrDefault();
        bool isAtFirst = ( startIndex == 0 );
        bool isAtLast = ( ( scrollableColumns.Count - startIndex ) == neededScrollableColumns.Count );

        if( !fetchDirection.IsForward() )
        {
          if( isAtFirst != isAtLast )
          {
            isAtFirst = isAtLast;
            isAtLast = !isAtFirst;
          }

          neededScrollableContainers.Reverse();
          leftFixedContainer.Reverse();
          rightFixedContainer.Reverse();
        }


        LayoutFactoryBase scrollLayoutFactory = this.GetLayoutFactory( updateDetails.TransitionHint );
        LayoutFactoryBase fixedLayoutFactory = new LinearLayoutFactory( this.ContainerLayoutOrientation );
        ILayout scrollLayout = scrollLayoutFactory.CreateLayout( this.ColumnPanel.ScrollableSection, neededScrollableContainers );
        ILayout leftLayout = fixedLayoutFactory.CreateLayout( this.ColumnPanel.LeftFixedSection, leftFixedContainer );
        ILayout rightLayout = fixedLayoutFactory.CreateLayout( this.ColumnPanel.RightFixedSection, rightFixedContainer );

        this.ColumnPanel.SetColumns( leftLayout, scrollLayout, rightLayout );

        return new SourcePositionInfo( isAtFirst, isAtLast, firstItemReference, firstItemOffset, totalCount );

      }

      protected override ViewportInfo GetViewportInfo()
      {
        return this.ColumnPanel.GetViewportInfo(m_availableSize);
      }

      protected override int? NavigateCurrent( NavigationOperation navigation, out object newCurrent )
      {
        return this.NavigateInView( this.DataGridControl.CurrentColumn, navigation, out newCurrent );
      }

      protected override int? NavigateToElement( object element )
      {
        object newCurrent;
        return this.NavigateInView( ( Column )element, null, out newCurrent );
      }

      protected override ViewportPosition GetViewportFirstElementPosition()
      {
        ViewportInfo info = this.GetViewportInfo();
        object item = null;
        ILayoutElement firstElement = null;

        if( info.Elements.Count > 0 )
        {
          int minValue = info.Elements.Min( ( e ) => ( ( ColumnContainer )e ).Column.VisiblePosition );
          firstElement = info.Elements.FirstOrDefault( ( e ) => ( ( ColumnContainer )e ).Column.VisiblePosition == minValue );
        }

        if( firstElement != null )
        {
          item = firstElement.ReferenceItem;
        }

        return new ViewportPosition( item, true, new OffsetLength( info.FirstElementOffscreenLength, true ) );
      }

      private int? NavigateInView( Column currentColumn, NavigationOperation? navigation, out object newCurrent )
      {
        Debug.Assert( currentColumn != null );
        Debug.Assert( navigation == null || !navigation.Value.IsPage() );

        newCurrent = null;

        ViewportInfo viewInfo = this.GetViewportInfo();
        List<Column> visibleColumns = viewInfo.Elements.Select((e) => ((ColumnContainer)e).Column).ToList();
        int currentIndex = visibleColumns.IndexOf( currentColumn );

        if( currentIndex != -1 )
        {

          if( navigation != null )
          {
            if( navigation == NavigationOperation.NextItem )
            {
              currentIndex++;
            }
            else
            {
              currentIndex--;
            }

            if( currentIndex < 0 )
              return -1;
            else if( currentIndex == visibleColumns.Count )
              return 1;
          }

          if( currentIndex < 0
            || ( ( currentIndex == 0 ) && ( viewInfo.FirstElementOffscreenLength != 0d ) ) )
            return -1;
          if( ( currentIndex >= visibleColumns.Count )
            || ( ( currentIndex == visibleColumns.Count - 1 )
              && ( viewInfo.LastElementOffscreenLength != 0d ) ) )
          {
            return 1;
          }

          newCurrent = visibleColumns[ currentIndex ];
          return 0;
        }


        List<Column> scrollableColumns = this.DataGridControl.GetScrollableColumns().ToList();
        int scrollableIndex = scrollableColumns.IndexOf( currentColumn );

        if( scrollableIndex != -1 )
        {
          int compareResult = -1;
          if( visibleColumns.Count > 0 )
          {
            if( scrollableIndex > scrollableColumns.IndexOf( visibleColumns[ 0 ] ) )
            {
              compareResult = 1;
            }
          }

          return compareResult;
        }

        return 0;


      }

      protected override void UpdateScrollPosition( SourcePositionInfo newPositionInfo )
      {
        // Update the viewport size
        if( this.DataGridControl.m_horizontalScrollBar != null )
        {
          this.DataGridControl.m_horizontalScrollBar.Scroll -= this.DataGridControl.OnHorizontalScroll;

          this.UpdatePositionInformation( this.DataGridControl.m_horizontalScrollBar, newPositionInfo );

          this.DataGridControl.UpdateHorizontalScrollBarVisibility();

          this.DataGridControl.m_lastHorizontalValue = this.DataGridControl.m_horizontalScrollBar.Value;
          this.DataGridControl.m_horizontalScrollBar.Scroll += this.DataGridControl.OnHorizontalScroll;
        }
      }

      private int m_layoutFirstColumnIndex;//0
      private Size m_availableSize;
    }


  }
}
